<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>

    <script>
        /*
        众所周知 JS 是门非阻塞单线程语言，因为在最初 JS 就是为了和浏览器交互而诞生的。
        如果 JS 是门多线程的语言话，我们在多个线程中处理 DOM 就可能会发生问题
        （一个线程中新加节点，另一个线程中删除节点），当然可以引入读写锁解决这个问题。
        JS 在执行的过程中会产生执行环境，这些执行环境会被顺序的加入到执行栈中。
        如果遇到异步的代码，会被挂起并加入到 Task（有多种 task） 队列中。一旦执行栈为空，
        Event Loop 就会从 Task 队列中拿出需要执行的代码并放入执行栈中执行，
        所以本质上来说 JS 中的异步还是同步行为。
        */

        console.log('script start');
        setTimeout(function () {
            console.log('setTimeout');
        }, 0);
        console.log('script end');
        /**
            script start
            script end
            setTimeout
        */

        /*
            以上代码虽然 setTimeout 延时为 0，其实还是异步。这是因为 HTML5 标准规定这个函数第二个参数
            不得小于 4 毫秒，不足会自动增加。所以 setTimeout 还是会在 script end 之后打印。
            不同的任务源会被分配到不同的 Task 队列中，任务源可
            以分为 微任务（microtask） 和 宏任务（macrotask）。
            在 ES6 规范中，microtask 称为 jobs，macrotask 称为 task。
        */
        console.log('script start');
        setTimeout(function () {
            console.log('setTimeout');
        }, 0);
        new Promise((resolve) => {
            console.log('Promise')
            resolve()
        }).then(function () {
            console.log('promise1');
        }).then(function () {
            console.log('promise2');
        });

        console.log('script end');
        // script start => Promise => script end => promise1 => promise2 => setTimeout

        /*
        以上代码虽然 setTimeout 写在 Promise 之前，
        但是因为 Promise 属于微任务而 setTimeout 属于宏任务，所以会有以上的打印。   
        微任务包括 process.nextTick ，promise ，Object.observe ，MutationObserver
        宏任务包括 script ， setTimeout ，setInterval ，setImmediate ，I/O ，UI rendering
        很多人有个误区，认为微任务快于宏任务，其实是错误的。因为宏任务中包括了 script ，
        浏览器会先执行一个宏任务，接下来有异步代码的话就先执行微任务。
        */

        //所以正确的一次 Event loop 顺序是这样的↓↓↓↓↓↓↓↓↓↓↓
        /*
        1.执行同步代码，这属于宏任务
        2.执行栈为空，查询是否有微任务需要执行
        3.执行所有微任务
        4.必要的话渲染 UI
        5.然后开始下一轮 Event loop，执行宏任务中的异步代码
        */
        

    </script>
</body>

</html>